#include "TwoPointCrossover.h"
#include "../../Random/MyRandom.h"
#include <algorithm>


template <typename T>
TwoPointCrossover<T>::TwoPointCrossover()
{
	throw Unsupported_template_exception();
}

TwoPointCrossover<PermutationEncodedSolution>::TwoPointCrossover()
{
}

TwoPointCrossover<BitEncodedSolution>::TwoPointCrossover()
{
}

TwoPointCrossover<ValueEncodedSolution<int> >::TwoPointCrossover()
{
}


void TwoPointCrossover<PermutationEncodedSolution>::cross(Solution *parent1, Solution *parent2, Solution* &child1, Solution* &child2)
{
    PermutationEncodedSolution* pesParent1 = (PermutationEncodedSolution*) parent1;
    PermutationEncodedSolution* pesParent2 = (PermutationEncodedSolution*) parent2;
    PermutationEncodedSolution* pesChild1 = new PermutationEncodedSolution();
    PermutationEncodedSolution* pesChild2 = new PermutationEncodedSolution();
    
    vector<int> parent1Solution = pesParent1->getSolution();
    vector<int> parent2Solution = pesParent2->getSolution();
	
    //if we don't meet the prerequisites for doing a singlepoint crossover, then abort
    if ((parent1Solution.size() != parent2Solution.size()) || (parent1Solution.size() == 0))
    	throw Invalid_solution_size_exception();
    	
    int crossoverPoint1 = MyRandom::randInt(0, parent1Solution.size() - 1);
    int crossoverPoint2 = MyRandom::randInt(crossoverPoint1, parent1Solution.size() - 1);
    
    // Make sure that they're not the same point
    while (crossoverPoint2 == crossoverPoint1)
    	crossoverPoint2 = MyRandom::randInt(0, parent1Solution.size() - 1);
    	
    for (int i = 0; i < crossoverPoint1; ++i)
    {
    	try
    	{
    		pesChild1->addElement(parent1Solution[i]);
    		pesChild2->addElement(parent2Solution[i]);
    	}
    	catch (Invalid_element_exception &e)
    	{
    		cout << "Unrecoverable error when trying to add elements to the children in a crossover" << endl;
    		exit(-1);
    	}
    }
    
    int count = 0;
   	for (unsigned int i = 0; i < parent2Solution.size() && count < crossoverPoint2 - crossoverPoint1; ++i)
   	{
    	try
    	{
    		pesChild1->addElement(parent2Solution[i]);
    		++count;
    	}
    	catch (Invalid_element_exception &e)
    	{
    		// No worries
    	}
    }
    
    count = 0;
   	for (unsigned int i = 0; i < parent2Solution.size() && count < crossoverPoint2 - crossoverPoint1; ++i)
   	{
    	try
    	{
    		pesChild2->addElement(parent1Solution[i]);
    		++count;
    	}
    	catch (Invalid_element_exception &e)
    	{
    		// No worries
    	}
   	}
   	
   	for (unsigned int i = 0; i < parent1Solution.size(); ++i)
   	{
   		try
   		{
   			
   			pesChild2->addElement(parent2Solution[i]);
   		}
   		catch (Invalid_element_exception &e)
   		{
   			// S'all good
   		}
   		
   		try
   		{
   			pesChild1->addElement(parent1Solution[i]);
   		}
   		catch (Invalid_element_exception &e)
   		{
   			// That's fine
   		}
   	}
   	
   	child1 = pesChild1;
   	child2 = pesChild2;
}

void TwoPointCrossover<BitEncodedSolution>::cross(Solution *parent1, Solution *parent2, Solution* &child1, Solution* &child2)
{
    BitEncodedSolution* besParent1 = (BitEncodedSolution*) parent1;
    BitEncodedSolution* besParent2 = (BitEncodedSolution*) parent2;
    BitEncodedSolution* besTmp1 = (BitEncodedSolution*) parent1->clone();
    BitEncodedSolution* besTmp2 = (BitEncodedSolution*) parent2->clone();
    
    //find the crosspoints
    unsigned int minLength = min(besParent1->getSize(), besParent2->getSize());
    unsigned int firstCrossPoint = MyRandom::randInt(0, minLength - 1);
    unsigned int secondCrossPoint = MyRandom::randInt(firstCrossPoint, minLength - 1);
    
    //cross the two solutions at those crosspoints
    for (unsigned int i = firstCrossPoint; i <= secondCrossPoint; ++i) {
        besTmp1->setBit(i, besParent2->getBit(i));
        besTmp2->setBit(i, besParent1->getBit(i));
    }
    
    child1 = besTmp1;
    child2 = besTmp2;
}

void TwoPointCrossover<ValueEncodedSolution<int> >::cross(Solution *parent1, Solution *parent2, Solution* &child1, Solution* &child2)
{
    ValueEncodedSolution<int>* vesParent1 = (ValueEncodedSolution<int>*) parent1;
    ValueEncodedSolution<int>* vesParent2 = (ValueEncodedSolution<int>*) parent2;
    ValueEncodedSolution<int>* vesTmp1 = (ValueEncodedSolution<int>*) parent1->clone();
    ValueEncodedSolution<int>* vesTmp2 = (ValueEncodedSolution<int>*) parent2->clone();
    
    //find the crosspoints
    unsigned int minLength = min(vesParent1->getSize(), vesParent2->getSize());
    unsigned int firstCrossPoint = MyRandom::randInt(0, minLength - 1);
    unsigned int secondCrossPoint = MyRandom::randInt(firstCrossPoint, minLength - 1);
    
    //cross the two solutions at those crosspoints
    for (unsigned int i = firstCrossPoint; i <= secondCrossPoint; ++i) {
        vesTmp1->setElement(i, vesParent2->getElement(i));
        vesTmp2->setElement(i, vesParent1->getElement(i));
    }
    
    child1 = vesTmp1;
    child2 = vesTmp2;
}

template class TwoPointCrossover<PermutationEncodedSolution>;
template class TwoPointCrossover<BitEncodedSolution>;
template class TwoPointCrossover<ValueEncodedSolution<int> >;
